package com.free.semantic.core.impl.demo;

import com.free.semantic.common.*;
import com.free.semantic.core.ConceptInstanceManage;
import com.free.semantic.core.ConceptManage;
import com.free.semantic.core.FunctionProvider;
import com.free.semantic.core.adaptor.impl.ModelAdaptorImpl;
import com.free.semantic.core.impl.BaseTest;
import com.free.semantic.core.impl.ConceptInstanceManageImpl;
import com.free.semantic.req.RelationDefineGroup;
import com.free.semantic.utils.Utils;
import org.junit.jupiter.api.Test;

import java.util.List;

import static com.free.semantic.common.RangeObj.more;

/**
 * 语义网络demo演示，希望可以通过这个例子认识到两点
 * 1. 不依赖不同的系统的API名称参数等信息，而依赖于所描述事物背后的模型进行信息交换是可能的
 * 2. 模型名称差异下 实现信息互通
 */
public class Demo1_provinceAndCity extends BaseTest {

    /**
     * 名称不同的样例展示
     */
    @Test
    public void test() {

        /**
         * 创建第一个语义模型 实现模型
         */
        RelationDefineGroup group = new RelationDefineGroup();
        // relation 包含(A,B)
        defineRelation(group, "包含",
                defineItem(oneObj("A"), moreObj("B"))); //B->A=1  //代表 针对任意一个B，都可以找到 1个A与之对应

        //  使用包含关系 定义一个语义网络
        ConceptManage model = getConceptManage(group);
        //object 省份 城市
        object(model, "省份", "城市");
        //包含(省份,城市)
        createRelation(model, "包含", "省份", "城市");

        //创建模型的一个实现，并且实现 省份查询城市  和  城市查询 省份 函数（添加一些测试数据）
        ConceptInstanceManage modelInstance = new ConceptInstanceManageImpl(model,
                buildMap("省份", "城市", new String[][]{
                        {"浙江", "杭州"}, {"浙江", "金华"},
                        {"山西", "长治"}, {"山西", "运城"},
                        {"广东", "广州"}, {"广东", "珠海"}}));

        List<RelationChain> relationChains = modelInstance.concept().findExactRelationChain(Ls.of("省份"), "城市");

        // 利用模型实例进行查询，从省份山西出发，查询 所涉及的所有城市信息
        List<String> city = modelInstance.takePath(Ls.of(new ParamNode("省份", "山西")), "城市");
        System.out.println(city); // 输出 长治，运城


        /**
         * 接口模型
         */
        RelationDefineGroup include = new RelationDefineGroup();
        //relation include(A,B)
        defineRelation(include, "include",
                defineItem(oneObj("A"), moreObj("B")), //A->B=[0,) //代表 针对任意一个A，都可以找到 [0,)个B与之对应
                defineItem(oneObj("B"), oneObj("A"))); //代表 针对任意一个B，都可以找到 1个A与之对应

        //object province city
        ConceptManage anotherModel = getConceptManage(include);
        object(anotherModel, "province", "city");

        //include(province,city)
        createRelation(anotherModel, "include", "province", "city");
        // 创建语义网络的一个无实现的一个空实例
        ConceptInstanceManage anotherModelInstance = new ConceptInstanceManageImpl(anotherModel);

        /**
         * 复制一份实现模型，经过调整后完全匹配接口模型
         */
        ConceptInstanceManage copy = Utils.copy(modelInstance);
        // 将接口模型和实现模型融合在一起
        // 此时网络为   province->city  省份->城市
        Utils.mergerConcept(copy, anotherModelInstance);
        // 先是调整为  province->省份->城市->city    ,然后保持关系和实现不变的情况 隐藏 省份->城市，调整为  province->city
        Utils.equalConceptMapAndHideMiddle(copy, Ls.of("province"), "city", Ls.of("省份"), "城市");

        // 将代理转换为 注册器，会和接口进行校验
        anotherModelInstance.registerFunction("default", copy);

        List<String> anotherCityList = anotherModelInstance.takePath(Ls.of(new ParamNode("province", "山西")), "city");
        System.out.println(anotherCityList); //[长治, 运城]

        assert city.equals(anotherCityList);
    }

    /**
     * 单网络内重命名处理，不存在实现报错用例
     */
    @Test
    public void test2() {

        /**
         * 创建第一个语义模型
         */
        RelationDefineGroup group = new RelationDefineGroup();
        // relation 包含(A,B)
        defineRelation(group, "包含",
                defineItem(oneObj("A"), moreObj("B")), //A->B=[0,) //代表 针对任意一个A，都可以找到 [0,)个B与之对应
                defineItem(oneObj("B"), oneObj("A"))); //B->A=1  //代表 针对任意一个B，都可以找到 1个A与之对应

        defineRelation(group, "相等",
                defineItem(oneObj("A"), oneObj("B")), //A->B=[0,) //代表 针对任意一个A，都可以找到 [0,)个B与之对应
                defineItem(oneObj("B"), oneObj("A"))); //B->A=1  //代表 针对任意一个B，都可以找到 1个A与之对应

        //  使用包含关系 定义一个语义网络
        ConceptManage model = getConceptManage(group);
        //object 省份 城市
        object(model, "省份", "城市", "city", "province");
        //包含(省份,城市)
        createRelation(model, "包含", "省份", "城市");

        createRelation(model, "相等", "城市", "city");

        createRelation(model, "相等", "省份", "province");

        //创建模型的一个实现，并且实现 省份查询城市  和  城市查询 省份 函数（添加一些测试数据）
        ConceptInstanceManage modelInstance = new ConceptInstanceManageImpl(model,
                buildMap("省份", "城市", new String[][]{
                        {"浙江", "杭州"}, {"浙江", "金华"},
                        {"山西", "长治"}, {"山西", "运城"},
                        {"广东", "广州"}, {"广东", "珠海"}})
//                , conceptEqual("城市", "city")
//                , conceptEqual("省份", "province")
        );

        // 利用模型实例进行查询，从省份山西出发，查询 所涉及的所有城市信息
        List<String> city = modelInstance.takePath(Ls.of(new ParamNode("省份", "山西")), "城市");
        System.out.println(city); // 输出 长治，运城

        try {
            List<String> city2 = modelInstance.takePath(Ls.of(new ParamNode("province", "山西")), "city");
            System.out.println(city2); // 输出 长治，运城
        } catch (Exception e) {
            assert e.getMessage().equals("不存在关系[被相等]:province->省份对应的实现");
        }
    }

    /**
     * 单网络内重命名处理
     */
    @Test
    public void test3() {

        /**
         * 创建第一个语义模型
         */
        RelationDefineGroup group = new RelationDefineGroup();
        // relation 包含(A,B)
        defineRelation(group, "包含",
                defineItem(oneObj("A"), moreObj("B")), //A->B=[0,) //代表 针对任意一个A，都可以找到 [0,)个B与之对应
                defineItem(oneObj("B"), oneObj("A"))); //B->A=1  //代表 针对任意一个B，都可以找到 1个A与之对应

        defineRelation(group, "相等",
                defineItem(oneObj("A"), oneObj("B")), //A->B=[0,) //代表 针对任意一个A，都可以找到 [0,)个B与之对应
                defineItem(oneObj("B"), oneObj("A"))); //B->A=1  //代表 针对任意一个B，都可以找到 1个A与之对应

        //  使用包含关系 定义一个语义网络
        ConceptManage model = getConceptManage(group);
        //object 省份 城市
        object(model, "省份", "城市", "city", "province");
        //包含(省份,城市)
        createRelation(model, "包含", "省份", "城市");

        createRelation(model, "相等", "城市", "city");

        createRelation(model, "相等", "省份", "province");

        //创建模型的一个实现，并且实现 省份查询城市  和  城市查询 省份 函数（添加一些测试数据）
        ConceptInstanceManage modelInstance = new ConceptInstanceManageImpl(model,
                buildMap("省份", "城市", new String[][]{
                        {"浙江", "杭州"}, {"浙江", "金华"},
                        {"山西", "长治"}, {"山西", "运城"},
                        {"广东", "广州"}, {"广东", "珠海"}})
                , conceptEqual("城市", "city")
                , conceptEqual("省份", "province")
        );

        // 利用模型实例进行查询，从省份山西出发，查询 所涉及的所有城市信息
        List<String> city = modelInstance.takePath(Ls.of(new ParamNode("省份", "山西")), "城市");
        System.out.println(city); // 输出 长治，运城

        List<String> city2 = modelInstance.takePath(Ls.of(new ParamNode("province", "山西")), "city");
        System.out.println(city2); // 输出 长治，运城

        List<RelationChain> relationChains = model.findAllRelationChain(Ls.of("province"), "city");
        print(relationChains);
        assert city.equals(city2);
    }


    /**
     * 通过注册的方式，无分支场景
     */
    @Test
    public void test4() {
        /**
         * 创建另外一个模型
         */
        RelationDefineGroup include = new RelationDefineGroup();
        //relation include(A,B)
        defineRelation(include, "include",
                defineItem(oneObj("A"), moreObj("B")), //A->B=[0,) //代表 针对任意一个A，都可以找到 [0,)个B与之对应
                defineItem(oneObj("B"), oneObj("A"))); //代表 针对任意一个B，都可以找到 1个A与之对应

        //object province city
        ConceptManage interfaceModel = getConceptManage(include);
        object(interfaceModel, "province", "city");

        //include(province,city)
        createRelation(interfaceModel, "include", "province", "city");
        // 创建语义网络的一个无实现的一个空实例
        ConceptInstanceManage emptyInstance = new ConceptInstanceManageImpl(interfaceModel);

        /**
         * 创建第一个语义模型
         */
        RelationDefineGroup group = new RelationDefineGroup();
        // relation 包含(A,B)
        defineRelation(group, "包含",
                defineItem(oneObj("A"), moreObj("B")), //A->B=[0,) //代表 针对任意一个A，都可以找到 [0,)个B与之对应
                defineItem(oneObj("B"), oneObj("A"))); //B->A=1  //代表 针对任意一个B，都可以找到 1个A与之对应

        //  使用包含关系 定义一个语义网络
        ConceptManage model = getConceptManage(group);
        //object 省份 城市
        object(model, "省份", "城市");
        //包含(省份,城市)
        createRelation(model, "包含", "省份", "城市");

        //创建模型的一个实现，并且实现 省份查询城市  和  城市查询 省份 函数（添加一些测试数据）
        ConceptInstanceManage implementModel = new ConceptInstanceManageImpl(model,
                buildMap("省份", "城市", new String[][]{
                        {"浙江", "杭州"}, {"浙江", "金华"},
                        {"山西", "长治"}, {"山西", "运城"},
                        {"广东", "广州"}, {"广东", "珠海"}}));

        List<RelationChain> relationChains = implementModel.concept().findExactRelationChain(Ls.of("省份"), "城市");

        // 利用模型实例进行查询，从省份山西出发，查询 所涉及的所有城市信息
        List<String> city = implementModel.takePath(Ls.of(new ParamNode("省份", "山西")), "城市");
        System.out.println(city); // 输出 长治，运城

        ModelAdaptorImpl modelAdaptor = new ModelAdaptorImpl();

        modelAdaptor.setInterfaceModel(emptyInstance);
        modelAdaptor.setImplementModel(implementModel);

        modelAdaptor.addEqualFormula(EqualFormula.parseSimple("province=省份"));
        modelAdaptor.addEqualFormula(EqualFormula.parseSimple("city=城市"));
        FunctionProvider build = modelAdaptor.build();

        emptyInstance.registerFunction("proxy", build);
        List<String> list = emptyInstance.takePath(Ls.of(new ParamNode("province", "山西")), "city");
        assert list.equals(city);
    }


    /**
     * 通过注册的方式，有分支场景
     */
    @Test
    public void test5() {
        /**
         * 创建另外一个模型
         */
        RelationDefineGroup include = new RelationDefineGroup();
        //relation include(A,B)
        defineRelation(include, "包含",
                defineItem(oneObj("A"), moreObj("B")));

        //object province city
        ConceptManage interfaceModel = getConceptManage(include);
        object(interfaceModel, "省份", "县城");

        //include(province,city)
        createRelation(interfaceModel, "包含", "省份", "县城");

        // 创建语义网络的一个无实现的一个空实例
        ConceptInstanceManage emptyInstance = new ConceptInstanceManageImpl(interfaceModel);

        /**
         * 创建第一个语义模型
         */
        RelationDefineGroup group = new RelationDefineGroup();
        // relation 包含(A,B)
        defineRelation(group, "包含",
                defineItem(oneObj("A"), moreObj("B"))); //B->A=1  //代表 针对任意一个B，都可以找到 1个A与之对应

        defineRelation(group, "是",
                defineItem(oneObj("A"), oneObj("B"))); //B->A=1  //代表 针对任意一个B，都可以找到 1个A与之对应


        //  使用包含关系 定义一个语义网络
        ConceptManage model = getConceptManage(group);
        //object 省份 城市
        object(model, "省份", "城市", "帮扶对象", "县城");
        //包含(省份,城市)
        createRelation(model, "包含", "省份", "城市");
        createRelation(model, "包含", "城市", "县城");

        createRelation(model, "包含", "省份", "帮扶对象");

        createRelation(model, "是", "帮扶对象", "县城");


        //创建模型的一个实现，并且实现 省份查询城市  和  城市查询 省份 函数（添加一些测试数据）
        ConceptInstanceManage implementModel = new ConceptInstanceManageImpl(model,
                buildMap("省份", "城市", new String[][]{
                        {"浙江", "杭州"}, {"浙江", "金华"}}),
                buildMap("城市", "县城", new String[][]{
                        {"杭州", "杭州县城A"}, {"金华", "金华县城C"},
                        {"杭州", "杭州县城B"}, {"金华", "金华县城D"}}),
                buildMap("省份", "帮扶对象", new String[][]{
                        {"浙江", "帮扶对象E"}, {"浙江", "帮扶对象H"}}),
                buildMap("帮扶对象", "县城", new String[][]{
                        {"帮扶对象E", "县城G"}, {"帮扶对象H", "县城F"}}));

        {
            List<RelationChain> relationChains = implementModel.concept().findAllRelationChain(Ls.of("省份"), "县城");
            assert relationChains.size() == 2;
        }
        {
            RelationChain goByNodeChain = implementModel.concept().findChainByLocatorNode(Ls.of(P("省份"), L("帮扶对象")), more("县城"));
            assert goByNodeChain != null;
        }

        List<String> result = implementModel.goByLocatorNode(Ls.of(
                new ParamNode("省份", "浙江"),
                new LocatorNode("帮扶对象")), more("县城"));

        System.out.println(result);

        ModelAdaptorImpl modelAdaptor = new ModelAdaptorImpl();
        modelAdaptor.setInterfaceModel(emptyInstance);
        modelAdaptor.setImplementModel(implementModel);

        modelAdaptor.addEqualFormula(EqualFormula.parseSimple("省份=省份"));
        modelAdaptor.addEqualFormula(EqualFormula.parseSimple("县城=县城"));

        try {
            FunctionProvider build = modelAdaptor.build();
        } catch (Exception e) {
            assert e.getMessage().contains("无法在实现网络中找到唯一一条实现路径，请在接口模型中添加分支概念");
        }

        interfaceModel.addLineObjectBetweenRelationItem(Ls.of("省份"), "县城", Ls.of(more("helpObj")));

        modelAdaptor.addEqualFormula(EqualFormula.parseSimple("helpObj=帮扶对象"));

        FunctionProvider build = modelAdaptor.build();

        emptyInstance.registerFunction("proxy", build);

        List<RelationChain> chainList = emptyInstance.concept().findAllRelationChain(Ls.of("省份"), "县城");

        List<String> list = emptyInstance.takePath(Ls.of(
                        new ParamNode("省份", "浙江")),
                "县城");
        assert list.equals(result);

    }

    @Test
    public void test6() {
        /**
         * 创建另外一个模型
         */
        RelationDefineGroup include = new RelationDefineGroup();
        //relation include(A,B)
        defineRelation(include, "包含",
                defineItem(oneObj("A"), moreObj("B")));

        //object province city
        ConceptManage interfaceModel = getConceptManage(include);
        object(interfaceModel, "省份", "县城");

        object(interfaceModel, "省份2", "县城2");

        //include(province,city)
        createRelation(interfaceModel, "包含", "省份", "县城");

        createRelation(interfaceModel, "包含", "省份2", "县城2");

        interfaceModel.conceptMergeTo(Ls.of("省份"), "县城", Ls.of("省份2"), "县城2");
        System.out.println(interfaceModel);

    }


}
